package com.ch.chatbot.domain.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.ch.chatbot.domain.common.DomainConstant;
import com.ch.chatbot.domain.common.TemplateModelFactory;
import com.ch.chatbot.domain.model.aggregates.AIAnswer;
import com.ch.chatbot.domain.model.aggregates.Choices;
import com.ch.chatbot.domain.model.dto.QADto;
import com.ch.chatbot.domain.model.enums.GptModelEnum;
import com.ch.chatbot.domain.model.po.QuestionEntity;
import com.ch.chatbot.domain.model.vo.ChatGptTemplate;
import com.ch.chatbot.domain.service.IQuestionService;
import com.ch.chatbot.domain.service.OpenAIService;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.assertj.core.util.Lists;
import org.junit.platform.commons.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.*;

@Slf4j
@Service
public class OpenAIServiceImpl implements OpenAIService {

    @Value("${chat-bot-api.openApiKey}")
    private String apiKey;
    @Autowired
    private TemplateModelFactory templateModelFactory;

    @Autowired
    private IQuestionService questionService;

    @Override
    public String doChatGpt1(String question, String type) {

        QuestionEntity questionEntity = new QuestionEntity();
        ChatGptTemplate chatGptTemplate = getGptTemplate(question, type);
        String reqStr = JSONUtil.toJsonStr(chatGptTemplate);
        log.info("OpenAIServiceImpl.req = {}", reqStr);
        long startMillis = System.currentTimeMillis();
        String resp = HttpRequest.post(DomainConstant.CHAT_GPT_URL)
                .header(DomainConstant.CONTENT_TYPE, DomainConstant.APPLICATION_JSON)
                .header(DomainConstant.AUTHORIZATION, "Bearer " + apiKey)
                .body(reqStr)
                .timeout(5 * 60 * 60)
                .execute().body();
        long endMillis = System.currentTimeMillis();
        String respStr = JSONUtil.toJsonStr(resp);
        log.info("OpenAIServiceImpl.resp = {}", respStr);

        if (StringUtils.isNotBlank(resp)) {
            AIAnswer aiAnswer = JSONUtil.toBean(resp, AIAnswer.class);
            questionEntity.setModel(aiAnswer.getModel());
            String answer = JSONUtil.toJsonStr(aiAnswer.getChoices());
            String answerStr = answer.replace("/n", "");
            questionEntity.setAnswer(answerStr);
        }
        int spendSecond = (int) (endMillis - startMillis) / 1000;

        questionEntity.setQuestion(question);
        questionEntity.setRequestTime(spendSecond);
        questionEntity.setChatKey(apiKey);
        questionEntity.setReqData(reqStr);
        questionEntity.setRespData(respStr);
        questionService.save(questionEntity);
        return questionEntity.getAnswer();
    }

    @Override
    public String sessionChat(String question, String userName) {
        ChatGptTemplate chatGptTemplate = getGptTemplate(question, "");
        List<QuestionEntity> questionEntities = questionService.queryLastQuestion(userName);
        StringBuilder problemBuilder = new StringBuilder();
        if (!CollectionUtils.isEmpty(questionEntities)) {
            List<QADto> qaDtoList = Lists.newArrayList();
            questionEntities.forEach(questionEntity -> {
                QADto qaDto = new QADto();
                String ques = userName + ":" + questionEntity.getQuestion();
                List<String> answers = Lists.newArrayList();
                if (StrUtil.isNotBlank(questionEntity.getAnswer())) {
                    JSONArray jsonArray = JSONUtil.parseArray(questionEntity.getAnswer());
                    Iterator<Object> iterator = jsonArray.stream().iterator();
                    while (iterator.hasNext()) {
                        Object next = iterator.next();
                        JSONObject jsonObject = JSONUtil.parseObj(next);
                        String text = jsonObject.getStr("text");
                        answers.add(DomainConstant.MARV + text + DomainConstant.SUFFIX);
                    }
                    qaDto.setAnswers(answers);
                }
                qaDto.setQuestion(ques);
                qaDtoList.add(qaDto);
            });
            for (QADto qaDto : qaDtoList) {
                problemBuilder.append(qaDto.getQuestion());
                List<String> answers = qaDto.getAnswers();
                for (String answer : answers) {
                    problemBuilder.append(answer);
                }
            }
        }
        problemBuilder.append(userName).append(":").append(question);
        String problemStr = problemBuilder.toString();
        chatGptTemplate.setPrompt(problemStr);
        String tempStr = JSONUtil.toJsonStr(chatGptTemplate);

        log.info("sessionChat.question = {}， username = {}, tempStr = {} ", problemStr, userName, tempStr);
        long startMillis = System.currentTimeMillis();
        System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2,SSLv3");
        String resp = HttpRequest.post(DomainConstant.CHAT_GPT_URL)
                .header(DomainConstant.CONTENT_TYPE, DomainConstant.APPLICATION_JSON)
                .header(DomainConstant.AUTHORIZATION, "Bearer " + apiKey)
                .body(tempStr)
                .timeout(5 * 60 * 60)
                .execute().body();
        long endMillis = System.currentTimeMillis();
        String respStr = JSONUtil.toJsonStr(resp);
        log.info("OpenAIServiceImpl.resp = {}", respStr);
        QuestionEntity questionEntity = new QuestionEntity();
        if (StringUtils.isNotBlank(resp)) {
            AIAnswer aiAnswer = JSONUtil.toBean(resp, AIAnswer.class);
            questionEntity.setModel(aiAnswer.getModel());
            String answer = JSONUtil.toJsonStr(aiAnswer.getChoices());
            String answerStr = answer.replaceAll("\n", "");
            questionEntity.setAnswer(answerStr);
        }
        int spendSecond = (int) (endMillis - startMillis) / 1000;

        questionEntity.setQuestion(question);
        questionEntity.setRequestTime(spendSecond);
        questionEntity.setChatKey(apiKey);
        questionEntity.setReqData(question);
        questionEntity.setRespData(respStr);
        questionEntity.setUserName(userName);
        questionService.save(questionEntity);
        return questionEntity.getAnswer();
    }


    public String doChatGpt2(String question) throws IOException {
        CloseableHttpClient httpClient = HttpClientBuilder.create().build();

        HttpPost post = new HttpPost("https://api.openai.com/v1/completions");
        post.addHeader("Content-Type", "application/json");
        post.addHeader("Authorization", "Bearer " + apiKey);

        String paramJson = "{\"model\": \"text-davinci-003\", \"prompt\": \"" + question + "\", \"temperature\": 0, \"max_tokens\": 1024}";

        StringEntity stringEntity = new StringEntity(paramJson, ContentType.create("text/json", "UTF-8"));
        post.setEntity(stringEntity);
        log.info("OpenAIServiceImpl.req = {}", JSONUtil.toJsonStr(stringEntity));
        CloseableHttpResponse response = httpClient.execute(post);
        log.info("OpenAIServiceImpl.resp = {}", JSONUtil.toJsonStr(response));
        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            String jsonStr = EntityUtils.toString(response.getEntity());
            AIAnswer aiAnswer = JSON.parseObject(jsonStr, AIAnswer.class);
            StringBuilder answers = new StringBuilder();
            List<Choices> choices = aiAnswer.getChoices();
            for (Choices choice : choices) {
                answers.append(choice.getText());
            }
            return answers.toString();
        } else {
            throw new RuntimeException("api.openai.com Err Code is " + response.getStatusLine().getStatusCode());
        }
    }

    private ChatGptTemplate getGptTemplate(String question, String type) {
        if ("QAA".equals(type)) {
            return templateModelFactory.getQuestionAndAnswerTemplate(question);
        } else if ("NE".equals(type)) {
            return templateModelFactory.normalEnglish(question);
        } else if ("CC".equals(type)) {
            return templateModelFactory.computeComplex(question);
        } else if ("EC".equals(type)) {
            return templateModelFactory.explainComplexCode(question);
        } else if ("RA".equals(type)) {
            return templateModelFactory.reluctantlyAnswer(question);
        } else if ("".equals(type)) {
            return templateModelFactory.reluctantlyAnswer("");
        }

        return templateModelFactory.normalHandle(question);
    }


}
